/*--------------------------------------------- Noble Steed Engine--------------------------------------*
Authors : Daniel Randle, Alex Bourne
Date Created : Mar 13 2013

File:
	nsobjectset.cpp

Description:
	This file contains the definition for the NSObjectSet class and any associated functions that help
	the ol' girl out
*-----------------------------------------------------------------------------------------------------*/

// Includes
#include <nsobjectset.h>
#include <nshextile.h>
#include <nslight.h>
#include <nsobjectreferenceset.h>
#include <nsshader.h>
#include <sstream>
#include <nsframebufferobject.h>

#ifdef DEBUG
#include <debug.h>
#endif



/*----------------------------*
|   class NSObjectSet BEGINS  |
*-----------------------------*/


NSObjectSet::NSObjectSet(NSMapArea * area): mapArea(area)
{
	LogFile("Creating object set...");
}

NSObjectSet::~NSObjectSet()
{
	LogFile("Deleting objects in object set...");
	Iterator iter = begin();
	while (iter != end())
	{
		while (iter->second.begin() != iter->second.end())
		{
			if (iter->second.back()->getCopies() == 1)
				delete iter->second.back();
			else
				iter->second.back()->removeCopy();
			iter->second.pop_back();
		}
		++iter;
	}
	LogFile("ObjectSet Deleted");
}

void NSObjectSet::addObject(NSShader* shader, NSObject *object)
{
	std::stringstream ss;
	
	object->addCopy();
	ss << "Adding object with name : " + object->getName() + " - number of copies : " << object->getCopies();
	LogFile l(ss);
	shaderMap[shader].push_back(object);
	if (object->isHasTransluscentMaterial())
		transObjects[shader].push_back(object);
}

NSObject * NSObjectSet::addObject(NSObject::ObjectType type, NSShader* shader, const std::string & name, const std::string & mfileName, const std::string & mDirectory, const std::string & texDir)
{
	NSObject * obj = NULL;
	switch (type)
	{
	case (NSObject::Base):
		obj = new NSObject(name, mfileName, mDirectory, texDir);
		break;
	case (NSObject::HexTile):
		obj = new NSHexTile(name, mfileName, mDirectory, texDir);
		break;
	case (NSObject::DirectionalLight):
		obj = new NSDirectionLight(name);
		break;
	case (NSObject::SpotLight):
		obj = new NSSpotLight(mfileName, mDirectory, texDir);
		break;
	case (NSObject::PointLight):
		obj = new NSPointLight(mfileName, mDirectory, texDir);
		break;
	}
	addObject(shader, obj);
	return obj;
}

bool NSObjectSet::changeObjectShader(const std::string & objectName, NSShader * newShader)
{
	return changeObjectShader(getObject(objectName), newShader);
}

bool NSObjectSet::changeObjectShader(NSObject* object, NSShader * newShader)
{
	std::map<NSShader*,std::vector<NSObject*>>::iterator iter = shaderMap.begin();
	while (iter != shaderMap.end())
	{
		std::vector<NSObject*>::iterator iter2 = iter->second.begin();
		while (iter2 != iter->second.end())
		{
			if ( (*iter2) == object )
			{
				LogFile("Changing shader of object name: " + object->getName() + " from ShaderID : " + iter->first->getShaderID() + " to Shader ID : " + newShader->getShaderID());
				iter2 = iter->second.erase(iter2);;
				addObject(newShader, object);
				return true;
			}
			++iter2;
		}
		++iter;
	}
	LogFile("Failed to change shader of object: " + object->getName());
	return false;
}

bool NSObjectSet::changeObjectsShader(NSShader * oldShader, NSShader * newShader)
{
	std::map<NSShader*,std::vector<NSObject*>>::iterator iter = shaderMap.begin();
	while (iter != shaderMap.end())
	{
		if (iter->first == oldShader)
		{
			std::vector<NSObject*>::iterator iter2 = iter->second.begin();
			while (iter2 != iter->second.end())
			{
				changeObjectShader((*iter2), newShader);
				++iter2;
			}
			return true;
		}
		++iter;
	}
	return false;
}

NSObject * NSObjectSet::getObject(const std::string & objectName)
{
	std::map<NSShader*,std::vector<NSObject*>>::iterator iter = shaderMap.begin();
	while (iter != shaderMap.end())
	{
		std::vector<NSObject*>::iterator iter2 = iter->second.begin();
		while (iter2 != iter->second.end())
		{
			if ((*iter2)->getName() == objectName)
				return *iter2;
			++iter2;
		}
		++iter;
	}
	LogFile("Failed to find and return object with name" + objectName);
	return NULL;
}

NSObject * NSObjectSet::getObject(unsigned int objectID)
{
	std::map<NSShader*,std::vector<NSObject*>>::iterator iter = shaderMap.begin();
	while (iter != shaderMap.end())
	{
		std::vector<NSObject*>::iterator iter2 = iter->second.begin();
		while (iter2 != iter->second.end())
		{
			if ((*iter2)->getID() == objectID)
				return *iter2;
			++iter2;
		}
		++iter;
	}
	LogFile("Failed to find and return object with ID");
	return NULL;
}

bool NSObjectSet::removeObject(const std::string & objectName)
{
	return removeObject(getObject(objectName));
}

bool NSObjectSet::removeObject(NSObject *object)
{
	std::map<NSShader*,std::vector<NSObject*>>::iterator iter = shaderMap.begin();
	while (iter != shaderMap.end())
	{
		std::vector<NSObject*>::iterator iter2 = iter->second.begin();
		while (iter2 != iter->second.end())
		{
			if ((*iter2) == object)
			{
				LogFile("Removing base object references with name : " + object->getName());
				if (!mapArea->removeAllReferencesOf(object))
					LogFile("Failed removing references of object name : " + object->getName());
				LogFile("Deleting base object with name : " + object->getName());
				delete object;
				iter2 = iter->second.erase(iter2);
				if ((*iter2)->isHasTransluscentMaterial())
					removeTransObject(*iter2);
				return true;
			}
			++iter2;
		}
		++iter;
	}
	LogFile("No objects removed on remove object call with object name: " + object->getName());
	return false;
}

bool NSObjectSet::removeObjectsShader(NSShader * toRemove)
{
	std::map<NSShader*,std::vector<NSObject*>>::iterator iter = shaderMap.begin();
	while (iter != shaderMap.end())
	{
		if (iter->first == toRemove)
		{
			std::vector<NSObject*>::iterator iter2 = iter->second.begin();
			while (iter2 != iter->second.end())
			{
				addObject(NULL, *iter2);
				++iter2;
			}
			iter = shaderMap.erase(iter);
			return true;
		}
		++iter;
	}
	return false;
}

bool NSObjectSet::removeTransObject(NSObject *object)
{
	Iterator iter = transObjects.begin();
	while (iter != transObjects.end())
	{
		ObjectIterator iter2 = iter->second.begin();
		while (iter2 != iter->second.end())
		{
			if ((*iter2) == object)
			{
				iter2 = iter->second.erase(iter2);
				return true;
			}
			++iter2;
		}
		++iter;
	}
	return false;
}

void NSObjectSet::setMapArea(NSMapArea * area)
{
	mapArea = area;
}

void NSObjectSet::updateRefs(NSObject* baseObj)
{
	mapArea->update(baseObj);
}


NSObjectSet::Iterator NSObjectSet::begin()
{
	return shaderMap.begin();
}

NSObjectSet::Iterator NSObjectSet::end()
{
	return shaderMap.end();
}

NSObjectSet::Iterator NSObjectSet::transBegin()
{
	return transObjects.begin();
}

NSObjectSet::Iterator NSObjectSet::transEnd()
{
	return transObjects.end();
}

NSObjectSet::ObjectIterator NSObjectSet::objectsBegin( NSShader *shader )
{
	Iterator iter = begin();
	while (iter != end())
	{
		if (iter->first == shader)
			return iter->second.begin();
		++iter;
	}
	LogFile("objectsBegin : Unable to find a valid object vector for the shader " + shader->getShaderID());
	return empty.begin();
}

NSObjectSet::ObjectIterator NSObjectSet::objectsEnd( NSShader * shader )
{
	Iterator iter = begin();
	while (iter != end())
	{
		if (iter->first == shader)
			return iter->second.end();
		++iter;
	}
	LogFile("objectsEnd : Unable to find a valid object vector for the shader " + shader->getShaderID());
	return empty.end();
}